#define	_GUN_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
//必须要加到这个地方才能消除shced.h里面的编译问题。看来后面还是分开来定义比较好
#define __USE_GNU
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <net/ethernet.h>     /* the L2 protocols */
#include <sys/time.h>
#include <time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <monetary.h>
#include <locale.h>

#ifdef ENABLE_BPF
#include <pcap/pcap.h>
#include <pcap/bpf.h>
//#include <linux/filter.h>
#endif

#include "pfring.h"
//#include "pfutils.c"

#define ALARM_SLEEP             1
#define DEFAULT_SNAPLEN       8192
#define MAX_NUM_THREADS        64
#define DEFAULT_DEVICE     "eth1"
#define NO_ZC_BUFFER_LEN     9000
#define DEFAULT_MAX_CAP_LEN	(64 * 1024)

typedef	struct
{
	int min_cap_len;
	int max_cap_len;
} dummyPkgInfo;
//信号处理函数
//pfring数据结构
static pfring *pd = NULL;
uint8_t add_drop_rule = 0;

static void
sigproc(int sig)
{
	static uint8_t do_shutdown = 0;//一个判断关闭的标记
	static int called = 0;
	fprintf(stderr,"Leaving...\n");
	if(called)
		return;
	else
		called = 1;
	do_shutdown = 1;
	//print_stats();//打印状态，这个实现很复杂暂时不用20140924
	pfring_breakloop(pd);
}
//将etheraddr字符串化的一个操作
static char hex[] = "0123456789ABCDEF";
static char *
etheraddr_string(const uint8_t *ep,char *buf)
{
	uint32_t i,j;
	char *cp = buf;
	if((j = *ep >> 4) != 0) {
		*cp++ = hex[j];
	} else {
		*cp++ = '0';
	}
	*cp++ = hex[*ep++ & 0xf];
	for(i = 5;(int)--i >= 0;) {
		*cp++ = ':';
		if((j = *ep >> 4) != 0) {
			*cp++ = hex[j];
		} else {
			*cp++ = '0';
		}
		*cp++ = hex[*ep++ & 0xf];
	}
	*cp = '\0';
	return buf;
}
//和绑定当前线程到一个核心有关
int bindthread2core(pthread_t thread_id, u_int core_id) {
#ifdef HAVE_PTHREAD_SETAFFINITY_NP
	cpu_set_t cpuset;
	int s;
	CPU_ZERO(&cpuset);
	CPU_SET(core_id, &cpuset);
	if((s = pthread_setaffinity_np(thread_id, sizeof(cpu_set_t), &cpuset)) != 0) {
		fprintf(stderr, "Error while binding to core %u: errno=%i\n", core_id, s);
		return(-1);
	} else {
		return(0);
	}
#else
	fprintf(stderr, "WARNING: your system lacks of pthread_setaffinity_np() (not core binding)\n");
	return(0);
#endif
}
static int
bind2core(uint32_t core_id)
{
	//看来pfcount中是通过0123之类的索引来绑定各个CPU的
	return (bindthread2core(pthread_self(),core_id));
}

//这个是对包规则的一个增加
void drop_packet_rule(const struct pfring_pkthdr *h) {
	const struct pkt_parsing_info *hdr = &h->extended_hdr.parsed_pkt;
	static int rule_id=0;

	if(add_drop_rule == 1) {
		hash_filtering_rule rule;
		memset(&rule, 0, sizeof(hash_filtering_rule));
		rule.rule_id = rule_id++;		
		rule.vlan_id = hdr->vlan_id;
		rule.proto = hdr->l3_proto;
		rule.rule_action = dont_forward_packet_and_stop_rule_evaluation;
		rule.host4_peer_a = hdr->ip_src.v4, rule.host4_peer_b = hdr->ip_dst.v4;
		rule.port_peer_a = hdr->l4_src_port, rule.port_peer_b = hdr->l4_dst_port;		
		if(pfring_handle_hash_filtering_rule(pd, &rule, 1 /* add_rule */) < 0)
			fprintf(stderr, "pfring_add_hash_filtering_rule(1) failed\n");
		else
			printf("Added filtering rule %d\n", rule.rule_id);
	} else {
		filtering_rule rule;
		int rc;
		memset(&rule, 0, sizeof(rule));	
		rule.rule_id = rule_id++;
		rule.rule_action = dont_forward_packet_and_stop_rule_evaluation;
		rule.core_fields.proto = hdr->l3_proto;
		rule.core_fields.shost.v4 = hdr->ip_src.v4, rule.core_fields.shost_mask.v4 = 0xFFFFFFFF;
		rule.core_fields.sport_low = rule.core_fields.sport_high = hdr->l4_src_port;
		rule.core_fields.dhost.v4 = hdr->ip_dst.v4, rule.core_fields.dhost_mask.v4 = 0xFFFFFFFF;
		rule.core_fields.dport_low = rule.core_fields.dport_high = hdr->l4_dst_port;
		if((rc = pfring_add_filtering_rule(pd, &rule)) < 0)
			fprintf(stderr, "pfring_add_hash_filtering_rule(2) failed\n");
		else
			printf("Rule %d added successfully...\n", rule.rule_id);
	}
}
/*
 * A faster replacement for inet_ntoa().
 */
static char*
_intoa(uint32_t addr, char* buf, uint16_t bufLen) {
	char *cp, *retStr;
	uint32_t byte;
	int n;
	cp = &buf[bufLen];
	*--cp = '\0';
	n = 4;
	do {
		byte = addr & 0xff;
		*--cp = byte % 10 + '0';
		byte /= 10;
		if(byte > 0) {
			*--cp = byte % 10 + '0';
			byte /= 10;
			if(byte > 0)
				*--cp = byte + '0';
		}
		*--cp = '.';
		addr >>= 8;
	} while (--n > 0);
	/* Convert the string to lowercase */
	retStr = (char*)(cp+1);
	return(retStr);
}
//地址转换实现函数
static char *
intoa(uint32_t addr)
{
	static char buf[sizeof "ff:ff:ff:ff:ff:ff:255.255.255.255"];
	return (_intoa(addr,buf,sizeof(buf)));
}
////这个是对包的一个处理
//是单线程情况下，入口处理函数
//static int32_t thiszone;
//注意第三个参数，相当与是从回调中获取的
static void
dummyProcesssPacket(const struct pfring_pkthdr *h, 
		const u_char *p, const u_char *user_bytes)
{
	//这里是包处理的核心API所有功能都在这里实现
	dummyPkgInfo *dpi = (dummyPkgInfo *)user_bytes;
	if(h->len >= dpi->min_cap_len && h->len <= dpi->max_cap_len) {
		printf(" [caplen=%d][len=%d][parsed_header_len=%d]"
				"IPv4[%s:%d->%s:%d]\n",
			h->caplen, h->len, h->extended_hdr.parsed_header_len,
			intoa(h->extended_hdr.parsed_pkt.ipv4_src),
			h->extended_hdr.parsed_pkt.l4_src_port,
			intoa(h->extended_hdr.parsed_pkt.ipv4_dst),
			h->extended_hdr.parsed_pkt.l4_dst_port);
	}
	//这里是对包的进一步解析，pf_ring为了防止重复解析是从L3和L4开始
	//进行解析的l3_offset and l4_offset
	if(unlikely(add_drop_rule)) {
		if(h->ts.tv_sec == 0) {
			pfring_parse_pkt((u_char*)p,
				(struct pfring_pkthdr*)h, 4, 0, 1);
		}
		drop_packet_rule(h);
	}
}
//这是执行包分析任务线程入口函数
void* packet_consumer_thread(void* _id)
{
	//在pfcount的实现中，如果wait_for_packet
	//为0则要调用sched_yield来让出CPU
	return(NULL);
}

////////////////////////////////////////////////////////////////////////////////////
int
main(int argc,char *argv[])
{
	int num_threads = 1;//默认线程1，直接就是主线程
	//这个和-a的参数有关等待包就是要等待包完成后再让出CPU
	uint8_t wait_for_packet = 1;
	char *device = DEFAULT_DEVICE;//设备类型字符串
	uint8_t mac_address[6] = {0};//mac地址
	char buf[32] = {0};//缓冲区
	char path[256] = {0};//路径的存放数组
	int snaplen = DEFAULT_SNAPLEN;
	int bind_core = -1;//pfcount中-g使用,和bind2core有关
	uint32_t flags = 0;//用于pfring_open的标识
	uint8_t use_extended_pkt_header = 1;//默认不处理扩展包
	uint8_t promisc = 1;//默认打开混杂模式
	uint8_t enable_hw_timestamp = 0;//默认不开启硬件时间戳模式
	uint8_t dont_strip_timestamps = 0;//默认从包中去除硬件时间戳
	//CPU的百分比的设置，可以通过pf_config进行配置
	uint16_t cpu_percentage = 0;
	//设置最小可捕获的包长度
	int min_cap_len = 0;
	//设置最大可捕获的包长度
	int max_cap_len = DEFAULT_MAX_CAP_LEN;
	//在正式启动前需要做很多一些设置工作
	if(5 == argc) {
		device = argv[1];
		snaplen = atoi(argv[2]);
		if(0 == snaplen) {
			snaplen = DEFAULT_SNAPLEN;
		}
		min_cap_len = atoi(argv[3]);
		max_cap_len = atoi(argv[4]);
		if(max_cap_len < min_cap_len || 0 == max_cap_len
			 || max_cap_len > DEFAULT_MAX_CAP_LEN) {
			max_cap_len = DEFAULT_MAX_CAP_LEN;
		}
	} else {
		fprintf(stderr,"usage:%s <eth dev> <snaplen>"
					" <min capture len[0~64*1024]> "
					"<max capture len[0~64*1024]>\n",argv[0]);
		return 1;
	}
	add_drop_rule = 0;//默认不使用过滤规则
	//对于CPU百分比的设置，防止程序占用过多的CPU
	if(wait_for_packet && (cpu_percentage > 0)) {
		if(cpu_percentage > 99)
			cpu_percentage = 99;
		pfring_config(cpu_percentage);
	}
	if(num_threads > 1) {
		//如果线程数大于1则需要将pf_ring设置为可重入的模式，这个时候
		//会使用信号量来实现一些临界资源的管理，对于性能会有影响造成
		//性能下降，只有多线程的情况下才使用
		flags |= PF_RING_REENTRANT;
	}
	if(use_extended_pkt_header) {
		//如果没有设置，PF_RING不填充pfring_pkthdr结构的extended_hdr区域
		//如果设置，这个extended_hdr区域也被完全填充。在这个情形下，你不需要
		//extended信息，设置它为0可提高操作速度。
		flags |= PF_RING_LONG_HEADER;
	}
	if(promisc) {
		//使设备在混杂模式下打开
		flags |= PF_RING_PROMISC;
	}
	if(enable_hw_timestamp) {
		//开启硬件时间戳，如果可用
		flags |= PF_RING_HW_TIMESTAMP;
	}
	if(!dont_strip_timestamps) {
		//从包中去除硬件时间戳
		flags |= PF_RING_STRIP_HW_TIMESTAMP;
	}
	/* Note that symmetric RSS is ignored by non-DNA drivers */
	//注意非DNA驱动会忽略对称接收端缩放技术
	flags |= PF_RING_DNA_SYMMETRIC_RSS;
	//正式打开设备
	pd = pfring_open(device,snaplen,flags);
	if(NULL == pd) {
		fprintf(stderr,"pfring_open %s error:%s\n",
						device,strerror(errno));
		return -1;
	} else {
		//打印下这个版本信息
	}
	//判断设备类型，如果是DNA驱动就直接开始用了，其它类型的
	//还需要做一些绑定操作。
	if(NULL != strstr(device,"dnacluster:")) {
		printf("Capturing from %s\n",device);
	} else {
		if(pfring_get_bound_device_address(pd, mac_address) != 0)
			fprintf(stderr, "Unable to read the device address\n");
		else {
			int ifindex = -1;
			pfring_get_bound_device_ifindex(pd, &ifindex);
			printf("Capturing from %s [%s][ifIndex: %d]\n",
			     device, etheraddr_string(mac_address, buf),
			     ifindex);
		}
	}
	printf("# Device RX channels: %d\n", pfring_get_num_rx_channels(pd));
	printf("# Polling threads:    %d\n", num_threads);
	//注册一些信号
	signal(SIGINT,sigproc);
	signal(SIGTERM,sigproc);

	pfring_set_application_stats(pd, "Statistics not yet computed: please try again...");
	if(pfring_get_appl_stats_file_name(pd, path, sizeof(path)) != NULL) {
		fprintf(stderr, "Dumping statistics on %s\n", path);
	}
	//使用pfring之前必须要使能它
	if (pfring_enable_ring(pd) != 0) {
		printf("Unable to enable ring :-(\n");
		pfring_close(pd);
		return(-1);
	}

	if (num_threads <= 1) {
		//绑定核心，如果设置了序号就进行绑定，否则由内核选择
		if(bind_core >= 0) {
			bind2core(bind_core);
		}
		//这个是用来打开进入loop的一个操作
		dummyPkgInfo dpi;
		dpi.min_cap_len = min_cap_len;
		dpi.max_cap_len = max_cap_len;
		pfring_loop(pd, dummyProcesssPacket, 
			(u_char*)&dpi, wait_for_packet);
		//packet_consumer_thread(0);
	} else {
		pthread_t my_thread;
		long i;
		//一个线程一个业务的模式如果无锁，性能应该很高
		for(i=0; i<num_threads; i++) {
			pthread_create(&my_thread, NULL, packet_consumer_thread, (void*)i);
		}
		for(i=0; i<num_threads; i++) {
			pthread_join(my_thread, NULL);
		}
	}
	sleep(1);
	pfring_close(pd);
	return 0;
}
